/*
Copyright (C) 2022-2024 Inspur Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/
package utils

import (
	"bufio"
	"crypto/tls"
	"errors"
	"fmt"
	"icep/config"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"strings"
	"time"
)

var (
	ErrUnknownDownloadStrategy = errors.New("unknown download strategy")
	ErrUrlUnreachable          = errors.New(" download  url not available")
	ErrReadAllFailure          = errors.New("ioutil readall  error")
)

// DlFileInfo 代表一个下载对象
type DlFileInfo struct {
	Url      string      //文件的网络下载地址
	Filename string      //下载文件的保存绝对路径
	Filemode os.FileMode //下载文件的保存mode
	Strategy int         //文件下载方式，可选值为1，2
}

func Download(df DlFileInfo) error {
	var cli = http.Client{}
	useSsl := config.GetBool("icep.upload.minio.use-ssl")
	if useSsl || strings.HasPrefix(df.Url, "https://") {
		cli.Transport = &http.Transport{
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: true,
			},
			Proxy: http.ProxyFromEnvironment,
			DialContext: (&net.Dialer{
				Timeout:   30 * time.Second,
				KeepAlive: 30 * time.Second,
				DualStack: true,
			}).DialContext,
			MaxIdleConns:          100,
			IdleConnTimeout:       90 * time.Second,
			TLSHandshakeTimeout:   10 * time.Second,
			ExpectContinueTimeout: 1 * time.Second,
		}
	}
	if df.Strategy == 1 {
		// strategy 为1表示先读取全部后直接写入文件
		return downloadStraightly(df, cli)
	} else if df.Strategy == 2 {
		// strategy 为2表示使用io.Copy进行基于文件缓冲区的读写
		return downloadWithBuf(df, cli)
	} else {
		return ErrUnknownDownloadStrategy
	}
}

// downloadStraightly 先读取全部的文件内容，放入slice,后将slice写入到新文件中
func downloadStraightly(df DlFileInfo, cli http.Client) error {
	res, err := cli.Get(df.Url)
	if err != nil {
		return ErrUrlUnreachable
	}
	defer res.Body.Close()

	// 如果返回值不是200，表示下载失败
	if res.StatusCode != 200 {
		return errors.New(res.Status)
	}

	// 如果文件很大，这个resBody slice将占用大量的内存
	resBody, err := ioutil.ReadAll(res.Body)
	if err != nil {
		fmt.Println("readall error:", err)
		return err
	}

	err = ioutil.WriteFile(df.Filename, resBody, df.Filemode)
	return err
}

// downloadWithBuf 使用io.Copy将数据写入到bufio实例化的writer中
func downloadWithBuf(df DlFileInfo, cli http.Client) error {
	res, err := cli.Get(df.Url)
	if err != nil {
		return ErrUrlUnreachable
	}
	defer res.Body.Close()

	// 如果返回值不是200，表示下载失败
	if res.StatusCode != 200 {
		return errors.New(res.Status)
	}

	//这里也可以简单使用包装好的函数 os.Create()
	f, err := os.OpenFile(df.Filename, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, df.Filemode)
	if err != nil {
		return err
	}
	defer f.Close()
	bufWriter := bufio.NewWriter(f)

	_, err = io.Copy(bufWriter, res.Body)
	if err != nil {
		return err
	}

	// 这里不要忘记最后把缓冲区中剩余的数据写入磁盘，默认情况下，4096byte后会自动进行一次磁盘写入
	// 比如文件为5000byte, flush会将缓冲区中的904byte写入磁盘中
	bufWriter.Flush()

	return nil
}
